/*
Copyright (C) 1996-1997 GX Media, Inc.
Copyright (C) 2010 Ronie Salgado

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

#include "stdafx.h"
#include "QTreeView.h"
#include "QooleDoc.h"
#include "QMainFrame.h"
#include "QooleId.h"

// QTreeView item data.
class QTreeItem: public wxTreeItemData
{
public:
	QTreeItem();
	~QTreeItem();

	enum ItemType
	{
		IT_DOCUMENT = 0,
		IT_OBJECT,
		IT_NOTHING,
	};

	QooleDoc *document;
	Object *object;
	ItemType type;
};

QTreeItem::QTreeItem()
{
	document = NULL;
	object = NULL;
	type = IT_NOTHING;
}

QTreeItem::~QTreeItem()
{
}

// Tree view icons.
#define TVICON_LEV			0
#define TVICON_QLE			1
#define TVICON_GROUP		2
#define TVICON_GROUPENT		3
#define TVICON_BRUSH		4
#define TVICON_BRUSHENT		5
#define TVICON_ENT			6
#define TVICON_DUMMY		7
#define TVICON_NO			8

BEGIN_EVENT_TABLE(QTreeView, wxTreeCtrl)
	EVT_SIZE(QTreeView::OnSize)
	EVT_TREE_ITEM_EXPANDING(QID_TREE_VIEW, QTreeView::OnTreeExpanding)
	EVT_TREE_ITEM_COLLAPSING(QID_TREE_VIEW, QTreeView::OnTreeCollapsing)
END_EVENT_TABLE();

QTreeView::QTreeView(wxWindow *parent)
{
	inUpdateView = false;
	currentDocument = NULL;

	// Create the tree control.
	Create(parent, QID_TREE_VIEW, wxDefaultPosition, wxSize(160, 300),
				wxTR_DEFAULT_STYLE | wxTR_HIDE_ROOT | wxTR_EDIT_LABELS);

	// Create the image list.
	wxImageList *list = new wxImageList(16, 16);
	list->Add(wxXmlResource::Get()->LoadIcon(wxT("tviconLev")));
	list->Add(wxXmlResource::Get()->LoadIcon(wxT("tviconQle")));
	list->Add(wxXmlResource::Get()->LoadIcon(wxT("tviconGroup")));
	list->Add(wxXmlResource::Get()->LoadIcon(wxT("tviconGroupEnt")));
	list->Add(wxXmlResource::Get()->LoadIcon(wxT("tviconBrush")));
	list->Add(wxXmlResource::Get()->LoadIcon(wxT("tviconBrushEnt")));
	list->Add(wxXmlResource::Get()->LoadIcon(wxT("tviconEnt")));
	list->Add(wxXmlResource::Get()->LoadIcon(wxT("tviconDummy")));
	list->Add(wxXmlResource::Get()->LoadIcon(wxT("tviconNo")));

	// Set the image list.
	AssignImageList(list);

	// Create the root item.
	rootItem = AddRoot(wxT("Root"), TVICON_QLE, TVICON_QLE);
}

QTreeView::~QTreeView()
{
}

void QTreeView::AddDocument(QooleDoc *document)
{
	// Check if the document is already here.
	DocumentItems::iterator it = documentItems.find(document);
	if(it != documentItems.end())
		return;

	// Check the root object.
	Object *root = document->GetRootObjectPtr();
	ASSERT(root);

	// Create a new node.
	QTreeItem *item = new QTreeItem();
	item->type = QTreeItem::IT_DOCUMENT;
	item->document = document;
	item->object = root;

	// Select an icon.
	int icon = TVICON_QLE;
	if(root->HasEntity() && root->GetEntityPtr()->IsWorldSpawn())
		icon = TVICON_LEV;

	// Make the document node.
	wxTreeItemId id = AppendItem(rootItem, document->GetTitle(), icon, icon, item);
	SetItemTextColour(id, *wxBLUE);
	SetItemText(id, document->GetTitle());

	// Add dummy node.
	AppendItem(id, wxT(""), TVICON_DUMMY, TVICON_DUMMY);

	// Store the item.
	documentItems.insert(std::make_pair(document, item));

	// Switch to the new document.
	SetCurrentDocument(document);
}

void QTreeView::RemoveDocument(QooleDoc *document)
{
	ASSERT(document);

	// Change the current document if necessary.
	if(currentDocument == document)
		SetCurrentDocument(NULL);

	// Find the document item.
	DocumentItems::iterator it = documentItems.find(document);
	if(it != documentItems.end())
	{
		// Delete the document item.
		QTreeItem *item = it->second;
		Delete(item->GetId());

		// Erase the document.
		documentItems.erase(it);
	}
}

void QTreeView::SetCurrentDocument(QooleDoc *document)
{
	if(currentDocument == document)
		return;

	// Remove view from the old document.
	QooleDoc *old = currentDocument;
	if(old)
	{
		// Collapse the old document folder.
		wxTreeItemId item = FindVisibleItem(old->GetRootObjectPtr());
		if(item)
		{
			CollapseSubTree(item);
			Collapse(item);
		}

		// Deselect the old document.
		Unselect();
	}

	// Store the new document.
	currentDocument = document;

	// Attach to the new document.
	RegisterDocument(document);

	if(currentDocument)
	{
		// Expand the new root level.
		wxTreeItemId item = FindVisibleItem(document->GetRootObjectPtr());
		if(item && !IsExpanded(item))
		{
			ExpandSubTree(item);
			Expand(item);
		}

		// Select the document node.
		SelectItem(item);
	}
}

void QTreeView::DeleteAllChildren(wxTreeItemId node)
{
	assert(node);

	// Delete all of the children of node.
	wxTreeItemIdValue cookie;
	wxTreeItemId item = GetFirstChild(node, cookie);
	while(item)
	{
		wxTreeItemId next = GetNextSibling(item);
		if(ItemHasChildren(item))
			DeleteAllChildren(item);
		Delete(item);
		item = next;
	}
}

void QTreeView::ExpandSubTree(wxTreeItemId node)
{
	assert(node);

	// Make sure it is expandable.
	QTreeItem *data = static_cast<QTreeItem*> (GetItemData(node));
	if(!data || !data->object)
		return;

	// Delete the children of node.
	DeleteAllChildren(node);

	// Add the children nodes.
	Object *object = data->object;
	assert(object);

	// Iterate the children.
	IterLinkList<Object> *iter = &object->GetIterChildren();
	iter->Reset();
	while(!iter->IsDone())
	{
		object = iter->GetNext();
		AddItemNode(node, object);
	}

	SortSubTree(node);
}

void QTreeView::CollapseSubTree(wxTreeItemId node)
{
	assert(node);

	// Make sure the node is valid and expanded.
	if(!IsExpanded(node))
		return;

	// Delete his children.
	DeleteAllChildren(node);

	// Get his object.
	QTreeItem *data = static_cast<QTreeItem*> (GetItemData(node));
	if(!data || !data->object)
		return;

	Object *object = data->object;
	assert(object);
	if(object->GetNumChildren() > 0)
	{
		// Add a dummy node.
		AppendItem(node, wxT(""), TVICON_DUMMY, TVICON_DUMMY);
	}
}

void QTreeView::SortSubTree(wxTreeItemId node)
{
	wxTreeItemId item;
	wxTreeItemIdValue cookie;
	for(item = GetFirstChild(node, cookie); item; item = GetNextSibling(item))
	{
		if(IsExpanded(item))
			SortSubTree(item);
	}

	SortChildren(node);
}

wxTreeItemId QTreeView::ExpandObjectTree(const Object *object)
{
	wxTreeItemId item;
	if(!object->IsRoot())
		item = ExpandObjectTree(object->GetParentPtr());
	else
		item = GetRootItem();

	wxTreeItemIdValue cookie;
	for(item = GetFirstChild(item, cookie); item; item = GetNextSibling(item))
	{
		QTreeItem *data = static_cast<QTreeItem*> (GetItemData(item));
		if(data->object == object)
			break;
	}

	assert(item);

	// Expand the item
	if(!IsExpanded(item))
	{
		ExpandSubTree(item);
		Expand(item);
	}

	// Return the item data.
	return item;
}

void QTreeView::RefreshSubTree(wxTreeItemId node)
{
	assert(node);

	// Prepare the compare list.
	QTreeItem *data = static_cast<QTreeItem*> (GetItemData(node));
	if(!data)
		return; // Ignore items without data.

	Object *object = data->object;
	LinkList<ObjectPtr> objPtrList;

	IterLinkList<Object> *iterPtr = &object->GetIterChildren();
	iterPtr->Reset();
	while(!iterPtr->IsDone())
	{
		object = iterPtr->GetNext();
		objPtrList.AppendNode(*(new ObjectPtr(object)));
	}

	ObjectPtr *objPtr;
	IterLinkList<ObjectPtr> iterPtrList(objPtrList);

	// Iterate through the tree nodes and dispose of corpses.
	wxTreeItemIdValue cookie;
	wxTreeItemId item = GetFirstChild(node, cookie);
	while(item)
	{
		data = static_cast<QTreeItem*> (GetItemData(item));
		object = data->object;

		// Compare with alive objects.
		bool alive = false;
		iterPtrList.Reset();
		while(!iterPtrList.IsDone())
		{
			objPtr = iterPtrList.GetNext();
			if(objPtr->GetPtr() == object)
			{
				alive = true;
				objPtrList.RemoveNode(*objPtr);
				delete objPtr;
				break;
			}
		}

		// Get first the next item.
		wxTreeItemId nextItem = GetNextSibling(item);

		if(!alive)
		{
			// Item has died.
			Delete(item);
		}

		item = nextItem;
	}

	// Add the left over into the tree.
	while(objPtrList.NumOfElm() > 0)
	{
		objPtr = &objPtrList.RemoveNode(0);
		AddItemNode(node, objPtr->GetPtr());
		delete objPtr;
	}

	// Sort for display.
	SortSubTree(node);

}

wxTreeItemId QTreeView::AddItemNode(wxTreeItemId parentNode, Object *object)
{
	assert(parentNode);
	assert(object);

	// Find the right icon.
	int image;
	if(object->GetNumChildren() > 0)
		image = !object->IsModelNode() ? TVICON_GROUP : TVICON_GROUPENT;
	else if(object->HasBrush())
		image = !object->IsModelNode() ? TVICON_BRUSH : TVICON_BRUSHENT;
	else if(object->IsItemNode())
		image = TVICON_ENT;
	else
		image = TVICON_DUMMY;

	// Insert the tree node.
	QTreeItem *data = new QTreeItem();
	data->object = object;
	data->type = QTreeItem::IT_OBJECT;
	wxTreeItemId ret = AppendItem(parentNode, object->GetObjName(), image, image, data);

	// Add a dummy child, to create the '+'.
	if(object->GetNumChildren() > 0)
		AppendItem(ret, wxT(""), TVICON_DUMMY, TVICON_DUMMY);

	return ret;
}

wxTreeItemId QTreeView::FindVisibleItem(const Object *object)
{
	if(!object)
		return wxTreeItemId();

	wxTreeItemId item = GetRootItem();
	while(item)
	{
		QTreeItem *data = static_cast<QTreeItem*> (GetItemData(item));
		if(data && data->object == object)
			return item;
		item = GetNextVisible(item);
	}

	return wxTreeItemId();
}

void QTreeView::UpdateSelection(wxTreeItemId item)
{
	Selector *selector = &GetMainFrame()->GetSelector();
	Object *object;
	QooleDoc *doc = GetMainFrame()->GetDeskTopDocument();
	wxTreeItemId root = FindVisibleItem(doc->GetRootObjectPtr());
	if(!root)
		return;

	if(selector->GetNumMSelectedObjects() == 0)
	{
		SelectItem(root, true);
	}
	else
	{
		SelectItem(root, false);

		if(selector->GetNumMSelectedObjects() != 1)
			UnselectAll();
	}

	wxTreeItemId cursor;
	wxTreeItemIdValue cookie;
	for(item = GetFirstChild(item, cookie); item; item = GetNextSibling(item))
	{
		QTreeItem *data = static_cast<QTreeItem*> (GetItemData(item));
		// Sanity check.
		if(!data || !data->object)
			continue;

		Object *object = data->object;
		bool selected = selector->IsObjectMSelected(object);
		if(selected)
			cursor = item;

		SelectItem(item, selected);
	}

	if(cursor)
		EnsureVisible(cursor);

	if(selector->GetNumMSelectedObjects() == 1 && cursor)
		SelectItem(cursor, true);
}

void QTreeView::UpdateScopeView(Object *oldScope)
{
	assert(oldScope);

	Selector *selector = &GetMainFrame()->GetSelector();
	Object *newScope = selector->GetScopePtr();

	wxTreeItemId item = FindVisibleItem(oldScope);
	assert(item);

	if(!oldScope->IsRoot())
	{
		// Need to find the top most unshared scope.
		oldScope = oldScope->GetParentPtr();
		while(!oldScope->IsRoot() && oldScope != newScope &&
				!newScope->IsMyAncestor(*oldScope))
		{
			oldScope = oldScope->GetParentPtr();
			item = GetItemParent(item);
		}

		// Collapse the old scope.
		CollapseSubTree(item);
		Collapse(item);
	}

	// Need to expand the new scope folder.
	ExpandObjectTree(newScope);
	assert(item);
}

void QTreeView::OnTreeExpanding(wxTreeEvent &ev)
{
	if(inUpdateView)
		return;

	wxTreeItemId item = ev.GetItem();
	ExpandSubTree(item);
}

void QTreeView::OnTreeCollapsing(wxTreeEvent &ev)
{
	if(inUpdateView)
		return;

	wxTreeItemId item = ev.GetItem();
	Selector *selector = &GetMainFrame()->GetSelector();
	QTreeItem *data = static_cast<QTreeItem*> (GetItemData(item));
	if(!data || !data->object)
		return;

	// Don't allow collapsing the scope or a parent of him.
	Object *object = data->object;
	if(selector->GetScopePtr() == object ||
		selector->GetScopePtr()->IsMyAncestor(*object))
		return;

	// Collapse the subtree.
	CollapseSubTree(item);
}

void QTreeView::OnUpdate(wxView* sender, wxObject* hint)
{
	int flags = 0;
	Object *scope = NULL;
	if(hint && hint->IsKindOf(CLASSINFO(QDocHint)))
	{
		QDocHint *qhint = static_cast<QDocHint*> (hint);
		flags = qhint->flags;
		scope = qhint->scope;
	}

	if(flags & DUAV_NOQTREEVIEW || scope == NULL)
		return;

	// Get the item, making sure it is visible.
	// Prevent expand/collapse events.
	inUpdateView = true;
	wxTreeItemId item = FindVisibleItem(scope);
	if(!item.IsOk())
	{
		item = ExpandObjectTree(scope);
	}

	if(flags & (DUAV_OBJSMODSTRUCT | DUAV_OBJSMODATTRIB))
	{
		// Refresh the folder.
		RefreshSubTree(item);

		EnsureVisible(item);
		flags |= DUAV_OBJSSEL;
	}

	if(flags & DUAV_OBJSSEL)
	{
		UpdateSelection(item);
	}

	if(flags & DUAV_SCOPECHANGED)
	{
		UpdateScopeView(scope);
	}

	if(flags & DUTV_RENAMEOBJ)
	{
		SetItemText(item, scope->GetObjName());
	}

	inUpdateView = false;
}

void QTreeView::OnSize(wxSizeEvent &ev)
{
	wxWindow *parent = GetParent();
	SetSize(parent->GetClientSize());
}


